package com.qianji.petmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.qianji.common.to.es.SkuEsModel;
import com.qianji.petmall.search.config.PetmallElasticSearch;
import com.qianji.petmall.search.constant.ESConstant;
import com.qianji.petmall.search.service.MallSearchService;
import com.qianji.petmall.search.vo.SearchParam;
import com.qianji.petmall.search.vo.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author liang zai
 * @date 2022/2/18 15:34
 * @description
 */
@Service
@Slf4j
public class MallSearchServiceImpl implements MallSearchService {
    @Autowired
    private RestHighLevelClient client;
    @Override
    public SearchResult seach(SearchParam searchParam) {
        SearchResult result = null;
        //构建方法，执行检索的条件
        SearchRequest request = buildSearchRequest(searchParam);
        try {
            SearchResponse search = client.search(request, PetmallElasticSearch.COMMON_OPTIONS);
            //封装成我们需要的格式
            result = buildSearchResult(search,searchParam);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 检索的语句和条件
     * 模糊匹配，过滤（属性，分类，价格区间，库存，品牌），排序，分页，高亮，聚合分析
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParam param) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //模糊匹配，过滤（属性，分类，价格区间，库存，品牌）
        //bool
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //must 模糊匹配
        if (StringUtils.isNotEmpty(param.getKeyword())){
            queryBuilder.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
        }
        //过滤的条件
        //分类 catalogId
        if (param.getCatalog3Id()!=null){
            queryBuilder.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
        }
        //品牌Id brandId
        if (param.getBrandId()!=null&&param.getBrandId().size()>0){
            queryBuilder.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
        }
        //库存 hasStock
        if (param.getHasStock()!=null){
            queryBuilder.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));
        }
        //价格区间 skuPrice
        if (StringUtils.isNotEmpty(param.getSkuPrice())){
            RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");
            //前端是1_500大于1小于500,_500 小于500,500_ 大于500
            String[] split = param.getSkuPrice().split("_");
            //split.length==1 就传入了一个值
            if (split.length==1){
                if (param.getSkuPrice().startsWith("_")){
                    skuPrice.lte(split[0]);
                }else {
                    skuPrice.gte(split[0]);
                }
            }else if (split.length==2){
                skuPrice.gte(split[0]);
                skuPrice.lte(split[1]);
            }
            queryBuilder.filter(skuPrice);

        }
        //nested attrs
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List<String> attrs = param.getAttrs();
        //遍历must
        if (attrs!=null&&attrs.size()!=0) {
            //attrs=1_5寸:8寸  &  2_16G:8G
            for (String builder : attrs) {
                String[] attrId = builder.split("_");
                boolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",attrId[0]));
                String[] attrValue = attrId[1].split(":");
                boolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",attrValue));
            }
        }
        queryBuilder.filter(QueryBuilders.nestedQuery("attrs",boolQueryBuilder, ScoreMode.None));
        //1.X bool query构建完成
        sourceBuilder.query(queryBuilder);

        //2.sort排序 eg:sort=saleCount_desc/asc
        if (StringUtils.isNotEmpty(param.getSort())){
            String[] sort = param.getSort().split("_");
            sourceBuilder.sort(sort[0], sort[1].contains("asc")?SortOrder.ASC:SortOrder.DESC);
        }
        //3. 分页 是检测结果分页
        sourceBuilder.from((param.getPageNumber() - 1) * ESConstant.PRODUCT_PAGESIZE);
        sourceBuilder.size(ESConstant.PRODUCT_PAGESIZE);
        ////4. 高亮highlight
        if (StringUtils.isNotEmpty(param.getKeyword())){
            HighlightBuilder highlighter = new HighlightBuilder();
            highlighter.field("skuTitle");
            highlighter.preTags("<b style='color:red'>");
            highlighter.postTags("</b>");
            sourceBuilder.highlighter(highlighter);
        }
//        String s = sourceBuilder.toString();
//        System.out.println("构建的DSL"+s);
        /**
         * 聚合
         */
        //TODO 5.1 按照brand聚合
        TermsAggregationBuilder builder = AggregationBuilders.terms("brandAgg").field("brandId").size(50);
        //根据名字聚合 子聚合
        TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName");
        //根据图片聚合 子聚合
        TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg").field("brandImg");
        builder.subAggregation(brandNameAgg);
        builder.subAggregation(brandImgAgg);
        sourceBuilder.aggregation(builder);

        //TODO 5.2 按照catalog聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalogAgg").field("catalogId").size(50);
        //根据catalogName名字聚合 子聚合
        TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName");
        catalogAgg.subAggregation(catalogNameAgg);
        sourceBuilder.aggregation(catalogAgg);

        //TODO 5.3 按照attrs聚合
        NestedAggregationBuilder nested = AggregationBuilders.nested("attrAgg", "attrs");
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(50);
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName");
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue");
        //TODO attrId的子聚合 子聚合的子聚合
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);
        //nested的子聚合
        nested.subAggregation(attrIdAgg);
        sourceBuilder.aggregation(nested);

        log.info("构建的DSL语句 {}",sourceBuilder.toString());
        SearchRequest request = new SearchRequest();
        request.source(sourceBuilder);
        return request;
    }

    /**
     * 封装成需要的格式
     * @param search
     * @return
     */
    private SearchResult buildSearchResult(SearchResponse search,SearchParam searchParam) {
        //需要返回的所有数据
        SearchResult result = new SearchResult();
        SearchHits hits = search.getHits();
//        //1.记录的所有商品信息
        List<SkuEsModel> skuEsModels = new ArrayList<>();
        if (hits.getHits()!=null&&hits.getHits().length>0){
            SearchHit[] hitsHits = hits.getHits();
            for (SearchHit hit : hitsHits) {
                String source = hit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(source, SkuEsModel.class);
                //高亮显示
                if (searchParam.getKeyword()!=null){
                    HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                    String highlight = skuTitle.getFragments()[0].toString();
                    skuEsModel.setSkuTitle(highlight);
                }
                skuEsModels.add(skuEsModel);
            }
        }
        result.setProducts(skuEsModels);
//        //2.当前查询到的结果所有的属性信息 聚合
        Aggregations aggregations = search.getAggregations();
        if (aggregations!=null){
            List<SearchResult.AttrVo> attrVos = new ArrayList<>();
            ParsedNested nested = aggregations.get("attrAgg");
            ParsedLongTerms attrAgg = nested.getAggregations().get("attrIdAgg");
            for (Terms.Bucket bucket : attrAgg.getBuckets()) {
                //5.1 查询属性id
                Long arrIds = bucket.getKeyAsNumber().longValue();
                //5.2 查询属性名
                ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
                String keyAsString = attrNameAgg.getBuckets().get(0).getKeyAsString();
                //5.3 查询属性值
                ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
                List<String> attrValue = new ArrayList<>();
                for (Terms.Bucket attrValueAggs : attrValueAgg.getBuckets()) {
                    String key = attrValueAggs.getKeyAsString();
                    attrValue.add(key);
                }
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo(arrIds, keyAsString, attrValue);
                attrVos.add(attrVo);
            }
            result.setAttrs(attrVos);
        }
//        //3.当前查询到的结果所有的品牌信息
        if (aggregations!=null){
            //所有的品牌信息聚合
            List<SearchResult.BrandVo> searchResults = new ArrayList<>();
            ParsedLongTerms brandAgg = aggregations.get("brandAgg");
            for (Terms.Bucket bucket : brandAgg.getBuckets()) {
                Aggregations bucketAggregations = bucket.getAggregations();
                //获取品牌的id
                Long brandId = bucket.getKeyAsNumber().longValue();
                //获取品牌的图片信息
                ParsedStringTerms brandImgAgg = bucketAggregations.get("brandImgAgg");
                String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
                //获取品牌名
                ParsedStringTerms brandNameAgg = bucketAggregations.get("brandNameAgg");
                String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo(brandId,brandName,brandImg);
                searchResults.add(brandVo);
            }
            result.setBrands(searchResults);
        }
//        //4.当前查询到的结果所有的分类
        if (aggregations!=null){
            List<SearchResult.CatalogVo> searchResults = new ArrayList<>();
            ParsedLongTerms catalogAgg = aggregations.get("catalogAgg");
            for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
                //获取分类的id
                Long cateLogId = bucket.getKeyAsNumber().longValue();
                //获取分类名
                ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalogNameAgg");
                String cateLogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo(cateLogId,cateLogName);
                searchResults.add(catalogVo);
            }
            result.setCatalogs(searchResults);
        }
//        /**
//         * 分页信息
//         */
//        //5.当前页码
        result.setPageNum(searchParam.getPageNumber());
//        //6.总记录数
        long total = hits.getTotalHits().value;
        result.setTotal(total);
//        //7.总页码数 页码计算页数
        long totalPage = total % ESConstant.PRODUCT_PAGESIZE == 0 ? total / ESConstant.PRODUCT_PAGESIZE : total / ESConstant.PRODUCT_PAGESIZE+1;
        result.setTotalPages(totalPage);

        List<Integer> page = new ArrayList<>();
        for (int i=1;i<=totalPage;i++){
            page.add(i);
        }
        result.setPageNavs(page);
        return result;
    }
}
